#ifndef PROTO_HEAD_H_
#define PROTO_HEAD_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <arpa/inet.h>
#include <assert.h>

enum C2CONN_CMD
{
	/* handshake cmd */
	ID_C2CONN_SYN = 0,
	ID_CONN2C_ACK,
	ID_C2CONN_ACK,

	/* notify in queue */
	ID_CONN2C_NTF_IN_QUEUE,
	
	/* notify complete connect */
	ID_CONN2C_NTF_CONNECT,

	/* notify server disconnect */
	ID_CONN2C_NTF_DISCONN,

	/* heartbeat to server */
	ID_C2CONN_NTF_HEARTBEAT,

	/* business forward */
	ID_C2CONN_NTF_PKG,
	ID_CONN2C_NTF_PKG,
};

enum CONN2S_CMD
{
	/* connect */
	ID_CONN2S_REQ_CONNECT = 0,
	ID_S2CONN_RES_CONNECT,

	/* business pkg */
	ID_CONN2S_NTF_PKG,
	ID_S2CONN_NTF_PKG,

	/* disconnect */
	ID_CONN2S_NTF_DISCONN,
	ID_S2CONN_NTF_DISCONN,
};

enum KICK_REASON
{
	KICK_VERSION_FAIL = 0,
	KICK_SERVER_DISCONNECT,
	KICK_SERVER_TIMEOUT,
};

typedef struct conn_head_t
{
	int16_t cmd;
	int16_t pkg_len;
	int32_t pkg_crc;

	/*
	*	reused as version when cmd = ID_C2CONN_SYN
	*	reused as encrypt key when cmd = ID_CONN2C_ACK | ID_C2CONN_ACK
	*	reused as queue index when cmd = ID_CONN2C_NTF_IN_QUEUE
	*	reused as kick reason when cmd = ID_CONN2C_NTF_DISCONN
	*/
	int32_t flag;
}conn_head_t;

typedef struct server_head_t
{
	int16_t cmd;
	int16_t pkg_len;
	/* 
	* reused as connect result when cmd = ID_S2CONN_RES_CONNECT, 0 means success, else means fail
	* reused as disconnect reason when cmd = ID_S2CONN_NTF_DISCONN
	*/
	int32_t pkg_crc;

	int32_t address;
	uint64_t timestamp;
}server_head_t;

static __attribute__((unused))  int serialize_conn_head(struct conn_head_t* head, char* buffer, size_t* bufflen)
{
	if(!head || !buffer || !bufflen)
		return -1;

	if(*bufflen < sizeof(struct conn_head_t))
		return -1;
	*bufflen = sizeof(struct conn_head_t);

	*(int16_t*)buffer = htons(head->cmd);
	*(int16_t*)(buffer + 2) = htons(head->pkg_len);
	*(int32_t*)(buffer + 4) = htonl(head->pkg_crc);
	*(int32_t*)(buffer + 8) = htonl(head->flag);
	return 0;
};

static __attribute__((unused))  int deserialize_conn_head(struct conn_head_t* head, char* buffer, size_t buffer_len)
{
	assert(head && buffer);
	assert(buffer_len == sizeof(struct conn_head_t));
	head->cmd = ntohs(*(int16_t*)buffer);
	head->pkg_len = ntohs(*(int16_t*)(buffer + 2));
	head->pkg_crc = ntohl(*(int32_t*)(buffer + 4));
	head->flag = ntohl(*(int32_t*)(buffer + 8));
	return 0;
};

static __attribute__((unused))  int serialize_server_head(struct server_head_t* head, char* buffer, size_t* bufflen)
{
	if(!head || !buffer || !bufflen)
		return -1;

	if(*bufflen < sizeof(struct server_head_t))
		return -1;
	*bufflen = sizeof(struct server_head_t);
	
	*(int16_t*)buffer = htons(head->cmd);
	*(int16_t*)(buffer + 2) = htons(head->pkg_len);
	*(int32_t*)(buffer + 4) = htonl(head->pkg_crc);
	*(int32_t*)(buffer + 8) = htonl(head->address);
	char* tmp = buffer + 12;
	*(uint32_t*)tmp = htonl((uint32_t)(head->timestamp >> 32));
	*(uint32_t*)(tmp + 4) = htonl((uint32_t)(head->timestamp & 0xFFFFFFFF));
	return 0;
};

static __attribute__((unused))  int deserialize_server_head(struct server_head_t* head, char* buffer, size_t bufferlen)
{
	assert(head && buffer);
	assert(bufferlen == sizeof(struct server_head_t));
	
	head->cmd = ntohs(*(int16_t*)buffer);
	head->pkg_len = ntohs(*(int16_t*)(buffer + 2));
	head->pkg_crc = ntohl(*(int32_t*)(buffer + 4));
	head->address = ntohl(*(int32_t*)(buffer + 8));
	uint32_t temp1 = *(uint32_t*)(buffer + 12);
	uint32_t temp2 = *(uint32_t*)(buffer + 16);
	head->timestamp = ((uint64_t)(ntohl(temp1)) << 32) | (uint64_t)(ntohl(temp2));
	return 0;
};


#ifdef __cplusplus
}
#endif

#endif // PROTO_HEAD_H_



